package com.jsy.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.jsy.common.vo.Result;
import com.jsy.goods.feign.SkuFeign;
import com.jsy.goods.pojo.Sku;
import com.jsy.search.dao.SkuEsMapper;
import com.jsy.search.pojo.SkuInfo;
import com.jsy.search.service.SkuEsService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.*;
import java.util.*;

@Service
public class SkuEsServiceImpl implements SkuEsService {
    @Autowired
    private SkuFeign skuFeign;
    @Autowired
    private SkuEsMapper skuEsMapper;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public void importSku() {
        //search data for es
        Result<List<Sku>> result = skuFeign.findByStatus("1");
        if (result.getData() != null) {
            String jsonString = JSON.toJSONString(result.getData());
            List<SkuInfo> skuInfoList = JSON.parseArray(jsonString, SkuInfo.class);
            for (SkuInfo skuInfo : skuInfoList) {
                String spec = skuInfo.getSpec();
                Map specMap = JSON.parseObject(spec, Map.class);
                skuInfo.setSpecMap(specMap);
            }
            //import es
            skuEsMapper.saveAll(skuInfoList);
        }
    }

    /**
     * 根据搜索条件搜索商品数据
     *
     * @param searchMap 搜索条件
     * @return 搜索结果
     */
    @Override
    public Map search(Map<String, String> searchMap) {
        HashMap<String, Object> resultMap = new HashMap<>();
        //创建查询条件构造对象
        NativeSearchQueryBuilder queryBuilder = createQueryBuilder(searchMap);
        //高亮查询
        AggregatedPage<SkuInfo> searchResult = searchSkuInfo(queryBuilder, searchMap);
        //商品分组统一处理
        groupSearch(queryBuilder, resultMap);
        //skuInfo列表
        resultMap.put("rows", searchResult.getContent());
        //总记录数
        resultMap.put("total", searchResult.getTotalElements());
        //页号
        resultMap.put("pageNum", searchResult.getPageable().getPageNumber());
        //页大小
        resultMap.put("pageSize", searchResult.getPageable().getPageSize());
        return resultMap;
    }

    /**
     * 高亮查询
     *
     * @param queryBuilder 查询条件设置
     * @param searchMap    高亮条件
     * @return 操作结果
     */
    private AggregatedPage<SkuInfo> searchSkuInfo(NativeSearchQueryBuilder queryBuilder, Map<String, String> searchMap) {
        //1.设置高亮条件
        //有搜索关键字才需要高亮
        if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
            //设置高亮域
            HighlightBuilder.Field highlightField = new HighlightBuilder.Field("name");
            //高亮起始标签
            highlightField.preTags("<span style='color:red'>");
            //高亮结束标签
            highlightField.postTags("</span>");
            //高亮内容长度
            highlightField.fragmentSize(100);
            //添加高亮域
            queryBuilder.withHighlightFields(highlightField);

            //2.查询，并对高亮结果进行处理
            AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    ArrayList<T> list = new ArrayList<>();
                    //处理每个对象
                    for (SearchHit hit : searchResponse.getHits()) {
                        //获取为高亮对象
                        SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                        //获取高亮域
                        HighlightField field = hit.getHighlightFields().get("name");
                        if (field != null) {
                            StringBuilder sb = new StringBuilder();
                            for (Text fragment : field.getFragments()) {
                                sb.append(fragment.toString());
                            }
                            skuInfo.setName(sb.toString());
                        }
                        list.add((T) skuInfo);
                    }
                    return new AggregatedPageImpl<>(list, pageable, searchResponse.getHits().getTotalHits());
                }
            });
            return aggregatedPage;
        }
        return elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class);
    }

    /**
     * 添加查询条件 过滤 分页 高亮 排序
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder createQueryBuilder(Map<String, String> searchMap) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        if (searchMap!=null){
            //添加查询条件
            if (!StringUtils.isEmpty(searchMap.get("keywords"))){
                queryBuilder.withQuery(QueryBuilders.matchQuery("name",searchMap.get("keywords")));
            }
            //创建过滤查询对象：因为过滤条件可能存在多个，所以创建组合查询对象
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            //分类过滤查询
            if (!StringUtils.isEmpty(searchMap.get("category"))){
                boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName",searchMap.get("category")));
            }
            //品牌过滤查询
            if (!StringUtils.isEmpty(searchMap.get("brand"))){
                boolQueryBuilder.must(QueryBuilders.matchQuery("brandName",searchMap.get("brand")));
            }

            /*规格的索引存储格式，真实数据在specMap.规格名字.keyword中，
            所以找数据也是按照如下格式去找：specMap.规格名字.keyword */
            //以spec_开头的就是规格过滤条件
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                if (entry.getKey().startsWith("spec_")){
                    //截取spec_之后的域名
                    String field = entry.getKey().substring(5);
                    String name="specMap."+field+".keyword";
                    boolQueryBuilder.must(QueryBuilders.matchQuery(name,entry.getValue()));
                }
            }
            //价格区间过滤
            if (!StringUtils.isEmpty(searchMap.get("price"))){
                String price = searchMap.get("price").replace("元", "").replace("以上","");
                String[] split = price.split("-");
                //价格上限
                String start=split[0];
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(start));
                if (split.length>1){
                    //价格下限
                    String end=split[1];
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lt(end));
                }
            }
            //分页设置
            int pageNum=1;
            if (!StringUtils.isEmpty(searchMap.get("pageNum"))){
                pageNum=Integer.parseInt(searchMap.get("pageNum"));
            }
            int pageSize=5;
            if (!StringUtils.isEmpty(searchMap.get("pageSize"))){
                pageSize=Integer.parseInt(searchMap.get("pageSize"));
            }
            //添加分页信息
            queryBuilder.withPageable(PageRequest.of(pageNum-1,pageSize));
            //搜索排序
            if (!StringUtils.isEmpty(searchMap.get("sortField"))&&!StringUtils.isEmpty(searchMap.get("sortRule"))){
                queryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.valueOf(searchMap.get("sortRule"))));
            }
            //添加过滤查询
            queryBuilder.withFilter(boolQueryBuilder);
        }
        return queryBuilder;
    }

    /**
     * 根据搜索条件按照商品统一分组搜索
     *
     * @param queryBuilder 搜索条件
     */
    private void groupSearch(NativeSearchQueryBuilder queryBuilder, HashMap<String, Object> resultMap) {
   /*
        分组统一搜索
        参数1：给分组起别名 skuCategory
        参数2：文档中要分组的域名 categoryName
         */
        queryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        queryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        queryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
        //分组查询
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class);
        //获取所有分组查询的数据
        Aggregations aggregations = aggregatedPage.getAggregations();
        //获取skuCategory的数据
        StringTerms categoryStringTerms = aggregations.get("skuCategory");
        //处理每个分组，将分组的值加入到list
        ArrayList<String> categoryList = new ArrayList<>();
        for (StringTerms.Bucket bucket : categoryStringTerms.getBuckets()) {
            String key = bucket.getKeyAsString();
            categoryList.add(key);
        }
        resultMap.put("categoryList",categoryList);
        //获取品牌分组数据
        StringTerms brandStringTerms = aggregations.get("skuBrand");
        List<String> brandList = new ArrayList<>();
        for (StringTerms.Bucket bucket : brandStringTerms.getBuckets()) {
            brandList.add(bucket.getKeyAsString());
        }
        resultMap.put("brandList",brandList);

        //获取规格分组数据
        StringTerms specStringTerms = aggregations.get("skuSpec");
        //循环所有规格数据，将每个规格存入到上述定义的map中
        Map<String, Set<String>> specMap = new HashMap<>();

        for (StringTerms.Bucket bucket : specStringTerms.getBuckets()) {
            Map<String,String> spec = JSON.parseObject(bucket.getKeyAsString(), Map.class);
            for (Map.Entry<String,String> entry : spec.entrySet()) {
                if (!specMap.containsKey(entry.getKey())){
                    specMap.put(entry.getKey(),new HashSet<>());
                }
                //将规格加入到该规格名称的集合
                specMap.get(entry.getKey()).add(entry.getValue());
            }
        }
        resultMap.put("specMap",specMap);
    }
}
